/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = ora_tti_dty.c                                      */
/*  NOTE      =                                                    */
/*  DATE      = 2014/10/14 by songbb                               */
/*******************************************************************/

#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>

#include "frcomm.h"
#include "frstr.h"
#include "frmem.h"
#include "frlog.h"

#include "conntrack.h"
#include "frct_comm.h"

#include "../ora_core.h"
#include "../comm/ora_comm.h"
#include "../comm/ora_util.h"
#include "ora_mar_engine.h"
#include "ora_tti_dty.h"
#include "ora_ttc_proto.h"

/*******************************************************************************
*  FUNC     :  测试数据内容
*  ARGS     :  s_len                                (数据长度)
*           :  *p_data                              (数据内容)
*           :  i_pos                                (位置)
*           :  i_value                              (校验值)
*           :  i_expect                             (期望的校验结果)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __ora_dty_test_capabilities(short s_len
                                    , char *p_data
                                    , int i_pos
                                    , int i_value
                                    , int i_expect)
{
    if (s_len == 0 || p_data == NULL || s_len - 1 < i_pos)
        return FR_FAIL;

    if ((p_data[i_pos] & i_value) == i_expect)
        return FR_SUCC;
    else
        return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  解析typeReps
*  ARGS     :  *p_st_sess                           (Oracle私有的session)
*           :  *p_st_ora_packet                     (Oracle数据包)
*           :  i_is_request                         (是否是请求)
*           :  s_CTcap_len                          (CTcap长度)
*           :  *p_CTcap                             (CTcap内容)
*           :  s_RTcap_len                          (RTcap长度)
*           :  *p_RTcap                             (RTcap内容)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __ora_dty_unmarshal_typeReps(L7session_ora *p_st_sess
                                        , L7_ora_packet *p_st_ora_packet
                                        , int i_is_request
                                        , short s_CTcap_len
                                        , char *p_CTcap
                                        , short s_RTcap_len
                                        , char *p_RTcap)
{
    int j = 0;
    int k = 0;
    int i_ttc_type_rep_tmp = 0;
    int i = 0;
    int i_d007_flag = FR_NO;

    if (__ora_dty_test_capabilities(s_CTcap_len, p_CTcap, 27, 0x01, 1) == FR_SUCC)
    {
        i_ttc_type_rep_tmp = ora_get_ttc_type_rep(p_st_sess, 1);
        ora_set_ttc_type_rep(p_st_sess, 1, 0);
    }
    if (__ora_dty_test_capabilities(s_RTcap_len, p_RTcap, 1, 0x01, 1) == FR_SUCC)
    {
        if (ora_mar_skip(p_st_ora_packet, 11) != FR_SUCC)       // TZBytes
            goto error;
        ORA_PRINT_NOTICE("dty skip 11 TZBytes\n");
        if (__ora_dty_test_capabilities(s_CTcap_len, p_CTcap, 37, 0x02, 2) == FR_SUCC && p_st_sess->s_tns_version >= 314)
        {
            ORA_PRINT_NOTICE("dty start to skip 4 bytes of timeZoneVersionNumber\n");
            if (ora_mar_skip(p_st_ora_packet, 4) != FR_SUCC)       // timeZoneVersionNumber
                goto error;
            ORA_PRINT_NOTICE("dty skip 4 bytes of timeZoneVersionNumber\n");
        }
        if (i_is_request && p_st_sess->i_use_oci == FR_YES && p_st_sess->s_tns_version >= 313)
        {
            ORA_PRINT_NOTICE("dty start to skip 2 bytes of 0xd007\n");
            if (ora_mar_skip(p_st_ora_packet, 2) != FR_SUCC)       // 0xd007
                goto error;
            i_d007_flag = FR_YES;
            ORA_PRINT_NOTICE("dty skip 2 bytes of 0xd007\n");
        }
    }

    if (bytebuff_remaining(p_st_ora_packet->p_st_packet) == 0)
    {
        if (__ora_dty_test_capabilities(s_CTcap_len, p_CTcap, 27, 0x01, 1) == FR_SUCC)
            ora_set_ttc_type_rep(p_st_sess, 1, i_ttc_type_rep_tmp);
        if (p_st_sess->i_use_oci == FR_YES && p_st_sess->i_detect_ptr_succ == FR_NO)
        {
            if (p_st_sess->c_ttc_version >= 6)
            {
                p_st_sess->i_ptr_len = APM_ORA_PTR_LEN_8;
                p_st_sess->i_send_typerep = FR_NO;
            }
            else
            {
                p_st_sess->i_ptr_len = APM_ORA_PTR_LEN_4;
                p_st_sess->i_send_typerep = FR_NO;
            }
            ORA_PRINT_CRIT("set i_send_typerep:[%d] i_ptr_len:[%d]\n", p_st_sess->i_send_typerep, p_st_sess->i_ptr_len);

            p_st_sess->i_detect_ptr_succ = FR_YES;
        }
        return FR_SUCC;
    }

    for (;;)
    {
        i = 0;
        if (p_st_sess->i_use_oci == FR_NO)
        {
            if (__ora_dty_test_capabilities(s_CTcap_len, p_CTcap, 27, 0x01, 1) == FR_SUCC)
            {
                if (ora_mar_unmarshalUB2(p_st_sess, p_st_ora_packet, &i) != FR_SUCC)
                    goto error;
            }
            else
            {
                if (ora_mar_unmarshalUB1(p_st_ora_packet, (short *) &i) != FR_SUCC)
                    goto error;
            }
        }
        else
        {
            if (i_d007_flag)
            {
                if (ora_mar_unmarshalUB2(p_st_sess, p_st_ora_packet, &i) != FR_SUCC)
                    goto error;
            }
            else
            {
                if (ora_mar_unmarshalUB1(p_st_ora_packet, (short *) &i) != FR_SUCC)
                    goto error;
            }
        }
        if (j == 0)
        {
            if (i == 0)
            {
                if (__ora_dty_test_capabilities(s_CTcap_len, p_CTcap, 27, 0x01, 1) == FR_SUCC)
                    ora_set_ttc_type_rep(p_st_sess, 1, i_ttc_type_rep_tmp);
                goto success;
            }
            j = 1;
        }
        else
        {
            switch (k)
            {
                case 0:
                    if (i == 0)
                        j = 0;
                    else
                        k = 1;
                    break;
                case 1:
                    k = 0;
            }
        }
    }

success:
    if (p_st_sess->i_use_oci == FR_YES && p_st_sess->i_detect_ptr_succ == FR_NO)
    {
        p_st_sess->i_ptr_len = APM_ORA_PTR_LEN_1;
        p_st_sess->i_send_typerep = FR_YES;
        if (p_st_sess->c_ttc_version >= 6)
            p_st_sess->i_ptr_len = APM_ORA_PTR_LEN_8;
        ORA_PRINT_CRIT("set i_send_typerep:[%d] i_ptr_len:[%d]\n", p_st_sess->i_send_typerep, p_st_sess->i_ptr_len);

        p_st_sess->i_detect_ptr_succ = FR_YES;
    }
    return FR_SUCC;
error:
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  解析dty包请求
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_sess                           (Oracle私有的session)
*           :  *p_st_ora_packet                     (Oracle数据包)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int ora_tti_dty_unpack_req(Conntrack *p_st_conn
                        , L7session_ora *p_st_sess
                        , L7_ora_packet *p_st_ora_packet)
{
    int i_tmp = 0;
    short s_cli_flags;
    short s_CTcap_len = 0;
    char *p_CTcap = NULL;
    short s_RTcap_len = 0;
    char *p_RTcap = NULL;

    int i = ora_get_ttc_type_rep(p_st_sess, 1);
    ora_set_ttc_type_rep(p_st_sess, 1, 2);

    if (ora_mar_unmarshalUB2(p_st_sess, p_st_ora_packet, &i_tmp) == FR_FAIL) // cliRIN(charset)
        goto error;
    p_st_sess->s_cli_charset_in = (short) i_tmp;

    if (ora_mar_unmarshalUB2(p_st_sess, p_st_ora_packet, &i_tmp) == FR_FAIL) // cliROUT(charset)
        goto error;
    p_st_sess->s_cli_charset_out = (short) i_tmp;

    ora_set_ttc_type_rep(p_st_sess, 1, i);

    if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_cli_flags) == FR_FAIL) // cliFlags
        goto error;
    p_st_sess->c_conversion_flags = (char) s_cli_flags;
    p_st_sess->i_CHR_conversion_flags = (p_st_sess->c_conversion_flags & 0x2) > 0 ? FR_YES : FR_NO;

    if (p_st_sess->c_pro_srv_version >= 6)
    {
        if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_CTcap_len) == FR_FAIL)                 // jdbcThinCompileTimeCapabilities.length
            goto error;

        if (ora_mar_unmarshalB1Array(s_CTcap_len, p_st_ora_packet, &p_CTcap) == FR_FAIL)    // jdbcThinCompileTimeCapabilities
            goto error;

        // 由于并不知道客户端的jdbcThinCompileTimeCapabilities配置，所以需要在这里将客户端发送的值取出来，然后再重新判断赋值给c_ttc_version
        if (s_CTcap_len > 7 && p_st_sess->c_ttc_version != APM_ORA_FLAG_UNSET && p_CTcap[7] != 0 && p_CTcap[7] < p_st_sess->c_ttc_version)
            p_st_sess->c_ttc_version = p_CTcap[7];
        ORA_PRINT_ALERT("ttc version final:[%d]\n", p_st_sess->c_ttc_version);

        if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_RTcap_len) == FR_FAIL)                 // jdbcThinRuntimeCapabilities.length
            goto error;

        if (ora_mar_unmarshalB1Array(s_RTcap_len, p_st_ora_packet, &p_RTcap) == FR_FAIL)    // jdbcThinRuntimeCapabilities
            goto error;
    }
    if (__ora_dty_unmarshal_typeReps(p_st_sess, p_st_ora_packet, FR_YES, s_CTcap_len, p_CTcap, s_RTcap_len, p_RTcap) != FR_SUCC)
        goto error;

    ora_free((void **) &(p_st_sess->p_CTcap));
    ora_free((void **) &(p_st_sess->p_RTcap));

    p_st_sess->s_CTcap_len = s_CTcap_len;
    p_st_sess->p_CTcap = p_CTcap;
    p_st_sess->s_RTcap_len = s_RTcap_len;
    p_st_sess->p_RTcap = p_RTcap;

    p_st_sess->i_ttc8_stmt_req_i = ((p_st_sess->i_use_special_field == FR_NO && p_st_sess->s_tns_version >= 308 && p_st_sess->c_ttc_version >= 4 && p_st_sess->s_CTcap_len >= 5 && p_st_sess->p_CTcap[4] >= 8) ||
            (p_st_sess->i_use_special_field == FR_YES && p_st_sess->s_tns_version >= 313)) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_req_i:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_req_i));

    p_st_sess->i_ttc8_stmt_req_j = ((p_st_sess->i_use_special_field == FR_NO && p_st_sess->c_ttc_version >= 5) || (p_st_sess->i_use_special_field == FR_YES && p_st_sess->s_tns_version >= 314)) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_req_j:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_req_j));

    p_st_sess->i_ttc8_stmt_req_dml = ((p_st_sess->i_use_special_field == FR_NO && p_st_sess->c_ttc_version >= 7) || (p_st_sess->i_use_special_field == FR_YES && p_st_sess->s_tns_version >= 315)) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_req_dml:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_req_dml));

    p_st_sess->i_ttc8_stmt_req_oci_after_dml = p_st_sess->i_use_special_field == FR_YES && p_st_sess->i_send_typerep == FR_NO && p_st_sess->s_tns_version >= 315 ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_req_oci_after_dml:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_req_oci_after_dml));

    p_st_sess->i_ttc8_stmt_resp_dcb_recv = p_st_sess->i_use_oci == FR_YES && p_st_sess->s_tns_version >= 314 ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_dcb_recv:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_dcb_recv));

    p_st_sess->i_ttc8_stmt_resp_oac_skip1 = p_st_sess->i_use_special_field == FR_YES && p_st_sess->i_send_typerep == FR_NO ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_oac_skip1:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_oac_skip1));

    p_st_sess->i_ttc8_stmt_resp_oac_skipaftervsn = p_st_sess->c_ttc_version >= 4 && p_st_sess->i_ptr_len == APM_ORA_PTR_LEN_8 ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_oac_skipaftervsn:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_oac_skipaftervsn));

    p_st_sess->i_ttc8_stmt_resp_udskpos = (p_st_sess->i_use_oci == FR_NO && p_st_sess->c_ttc_version >= 3) || (p_st_sess->i_use_oci == FR_YES && p_st_sess->s_tns_version >= 313) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_udskpos:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_udskpos));

    p_st_sess->i_ttc8_stmt_resp_udsflg = (p_st_sess->i_use_oci == FR_NO && p_st_sess->c_ttc_version >= 6) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_udsflg:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_udsflg));

    p_st_sess->i_ttc8_stmt_resp_dcbkm = (p_st_sess->i_use_special_field == FR_NO && p_st_sess->c_ttc_version >= 3) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_dcbkm:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_dcbkm));

    p_st_sess->i_ttc8_stmt_resp_dcbni1 = (p_st_sess->i_use_special_field == FR_NO && p_st_sess->c_ttc_version >= 4) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_dcbni1:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_dcbni1));

    p_st_sess->i_ttc8_stmt_resp_dcbab2 = (p_st_sess->i_use_special_field == FR_NO && p_st_sess->c_ttc_version >= 5) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_dcbab2:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_dcbab2));

    p_st_sess->i_ttc8_stmt_resp_rpag = ((p_st_sess->c_ttc_version >= 4) || (p_st_sess->s_tns_version >= 313)) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_rpag:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_rpag));

    p_st_sess->i_ttc8_stmt_resp_rpadml = p_st_sess->i_use_oci == FR_NO && p_st_sess->c_ttc_version >= 7 ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_rpadml:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_rpadml));

    if (((p_st_sess->i_use_special_field == FR_NO && p_st_sess->s_tns_version >= 310 && (
            (p_st_sess->c_ttc_version == 2 && p_st_sess->s_CTcap_len >= 5 && p_st_sess->p_CTcap[4] == 0 && p_st_sess->s_CTcap_len >= 16 && p_st_sess->p_CTcap[15] >= 41) ||
            (p_st_sess->s_CTcap_len >= 5 && p_st_sess->p_CTcap[4] >= 10)))
         ||
//            (p_st_sess->i_use_special_field == FR_YES && p_st_sess->s_tns_version >= 312)
            (p_st_sess->s_tns_version >= 312) // TODO:
        )
        &&  ora_has_svr_compile_time_capability(p_st_sess, 15, 1) == FR_SUCC)
        p_st_sess->i_ttc8_stmt_resp_oereocs = FR_YES;
    else
        p_st_sess->i_ttc8_stmt_resp_oereocs = FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_oereocs:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_oereocs));

    p_st_sess->i_ttc8_stmt_resp_oerendseq = (p_st_sess->i_use_special_field == FR_NO && p_st_sess->c_ttc_version >= 3) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_oerendseq:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_oerendseq));

//    p_st_sess->i_ttc8_stmt_resp_oeraij = (p_st_sess->i_use_special_field == FR_NO && p_st_sess->s_tns_version >= 310) ? FR_YES : FR_NO;
    p_st_sess->i_ttc8_stmt_resp_oci_oerskip8 = (p_st_sess->c_ttc_version >= 7 && p_st_sess->s_tns_version >= 315) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_oci_oerskip8:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_oci_oerskip8));

    p_st_sess->i_ttc8_stmt_resp_oci_oerskip6 = (p_st_sess->i_use_special_field == FR_YES && p_st_sess->i_send_typerep == FR_YES && p_st_sess->s_tns_version >= 315 && p_st_sess->c_ttc_version == 7) ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc8_stmt_resp_oci_oerskip6:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc8_stmt_resp_oci_oerskip6));

    p_st_sess->i_ttc7_oactoid = p_st_sess->s_ora_version >= 8030 ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc7_oactoid:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc7_oactoid));

    p_st_sess->i_ttc7_rxhrqsts = p_st_sess->s_ora_version >= 8030 ? FR_YES : FR_NO;
    ORA_PRINT_PARAM("i_ttc7_rxhrqsts:[%s]\n", FR_YESNO2STR(p_st_sess->i_ttc7_rxhrqsts));

    p_st_sess->i_ttc8_long_analyze_succ = FR_YES;

    if (ORA_CHECK_BIT(p_st_sess->p_srv_compile_time_capabilities, p_st_sess->s_srv_compile_time_capabilities_len, 23, 64) &&
            ORA_CHECK_BIT(p_st_sess->p_CTcap, p_st_sess->s_CTcap_len, 23, 64))
        p_st_sess->i_useLobPrefetch = FR_YES;
    else
        p_st_sess->i_useLobPrefetch = FR_NO;
    ORA_PRINT_PARAM("i_useLobPrefetch:[%s]\n", FR_YESNO2STR(p_st_sess->i_useLobPrefetch));

    return FR_SUCC;
error:
    ora_free((void **) &p_CTcap);
    ora_free((void **) &p_RTcap);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  解析dty包响应
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_sess                           (Oracle私有的session)
*           :  *p_st_ora_packet                     (Oracle数据包)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int ora_tti_dty_unpack_resp(Conntrack *p_st_conn
                            , L7session_ora *p_st_sess
                            , L7_ora_packet *p_st_ora_packet)
{
    if (ora_mar_skip(p_st_ora_packet, 1) != FR_SUCC)       // ttc code
        goto error;

    // 设置 arr_ttc_type_rep 的值
    ORA_PRINT_DBG("i_use_oci:[%s]\n", FR_YESNO2STR(p_st_sess->i_use_oci));
    ORA_PRINT_DBG("i_use_special_field:[%s]\n", FR_YESNO2STR(p_st_sess->i_use_special_field));
    if (p_st_sess->i_use_oci == FR_YES && p_st_sess->i_use_special_field == FR_YES)
    {
        if (p_st_sess->i_cli_is_bigendian == FR_YES)
        {
            if (p_st_sess->i_srv_is_bigendian == FR_NO)
                ora_set_ttc_type_rep_jdbc(p_st_sess);
            else
            {
                ora_set_ttc_type_rep(p_st_sess, 0, 0);
                ora_set_ttc_type_rep(p_st_sess, 1, 4);
                ora_set_ttc_type_rep(p_st_sess, 2, 4);
                ora_set_ttc_type_rep(p_st_sess, 3, 4);
                ora_set_ttc_type_rep(p_st_sess, 4, 0);
            }
        }
        else
        {
            ora_set_ttc_type_rep(p_st_sess, 0, 0);
            ora_set_ttc_type_rep(p_st_sess, 1, 2);
            ora_set_ttc_type_rep(p_st_sess, 2, 2);
            ora_set_ttc_type_rep(p_st_sess, 3, 2);
            ora_set_ttc_type_rep(p_st_sess, 4, 0);
        }
    }
    else
        ora_set_ttc_type_rep_jdbc(p_st_sess);

    if (__ora_dty_unmarshal_typeReps(p_st_sess, p_st_ora_packet, FR_NO, p_st_sess->s_CTcap_len, p_st_sess->p_CTcap, p_st_sess->s_RTcap_len, p_st_sess->p_RTcap) != FR_SUCC)
        goto error;

    return FR_SUCC;
error:
    return FR_FAIL;
}
